﻿using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Infrastructure.PlcCommon;

namespace Zocono.WCS.Domain.PlcConnectPoolDomain
{
    public class PlcConnetionPoolService : IPlcConnectionPoolService
    {
        private readonly ISnowflake _snowflake;
        private readonly ILogUtil _logUtil;
        private readonly IRedisHelper _IRedisHelper;
        private readonly IPlcConnectionInfoRepository _plcConnectionInfoRepository;
        private readonly IPlcReadDbInfoRepository _plcReadDbInfoRepository;
        private readonly IPlcConnectionLockInfoRepository _plcConnectionLockInfoRepository;
        private readonly IPlcConnectionStateInfoRepository _plcConnectionStateInfoRepository;
        private readonly IPlcConnectionPool _plcConnectionPool;
        private readonly IConfiguration _configuration;


        public PlcConnetionPoolService(IRedisHelper iRedisHelper, IConfiguration configuration, IPlcConnectionPool plcConnectionPool, IPlcConnectionInfoRepository plcConnectionInfoRepository, IPlcReadDbInfoRepository plcReadDbInfoRepository, IPlcConnectionLockInfoRepository plcConnectionLockInfoRepository, IPlcConnectionStateInfoRepository plcConnectionStateInfoRepository, ISnowflake snowflake, ILogUtil logUtil)
        {
            _configuration = configuration;
            _plcConnectionPool = plcConnectionPool;
            _plcConnectionInfoRepository = plcConnectionInfoRepository;
            _plcReadDbInfoRepository = plcReadDbInfoRepository;
            _plcConnectionLockInfoRepository = plcConnectionLockInfoRepository;
            _plcConnectionStateInfoRepository = plcConnectionStateInfoRepository;
            _snowflake = snowflake;
            _logUtil = logUtil;
            _IRedisHelper = iRedisHelper;
        }

        public async Task S7LoadAllPlcConnection(string programeCode)
        {
            //获取相应程序名对应的PLC连接的信息
            var connections = await _plcConnectionInfoRepository.GetPlcConnectionInfosAsync(programeCode);
            List<PlcConnectionInfo> plcConnectionInfos = new List<PlcConnectionInfo>();
            foreach (var conn in connections)
            {
                if (plcConnectionInfos.Exists(p => p.ID == conn.ID))
                {
                    continue;
                }
                plcConnectionInfos.Add(conn);
            }
            foreach (var item in plcConnectionInfos)
            {
                _logUtil.Info($"S7LoadAllPlcConnectionID={item.ID}-{item.IP}");
            }
            //加载所有连接到连接池中
            _plcConnectionPool.S7LoadAllPlcConnection(plcConnectionInfos);
        }

        public async Task S7LoadAllPlcConnections(List<string> programeCodes)
        {
            //获取相应程序名对应的PLC连接的信息
            foreach (var item in programeCodes)
            {
                //获取相应程序名对应的PLC连接的信息
                var connections = await _plcConnectionInfoRepository.GetPlcConnectionInfosAsync(item);
                List<PlcConnectionInfo> plcConnectionInfos = new List<PlcConnectionInfo>();
                foreach (var conn in connections)
                {
                    if (plcConnectionInfos.Exists(p => p.ID == conn.ID))
                    {
                        continue;
                    }
                    plcConnectionInfos.Add(conn);
                }
                foreach (var l in plcConnectionInfos)
                {
                    _logUtil.Info($"S7LoadAllPlcConnectionID={l.ID}-S7LoadAllPlcConnectionIP={l.IP}");
                    //_logUtil.Info($"S7LoadAllPlcConnectionIP={l.IP}");
                }
                //加载所有连接到连接池中
                _plcConnectionPool.S7LoadAllPlcConnection(plcConnectionInfos);
            }
        }

        public async Task S7ReLoadPlcConnection()
        {
            var reLoadPlcs = _plcConnectionPool.S7GetReLoadPlcConnection();
            if (reLoadPlcs == null)
            {
                return;
            }
            foreach (long id in reLoadPlcs)
            {
                //var conns = _IRedisHelper.GetStringKey<List<PlcConnectionInfo>>("PlcConnectionInfo");
                //var conn = conns.Where(l => l.ID == id).First();
                ////var conn = await _plcConnectionInfoRepository.GetByIdAsync(id);
                //_plcConnectionPool.S7ReLoadPlcConnection(id, conn);
                try
                {
                    var conns = _IRedisHelper.GetStringKey<List<PlcConnectionInfo>>("PlcConnectionInfo");
                    if (conns != null)
                    {
                        var conn = conns.Where(l => l.ID == id).FirstOrDefault();

                        _logUtil.Debug($"IP:{conn.IP}|EquipmentCode:{conn.ScEquipmentCode}|当前PLC状态未连接启用重连...");
                        //将断开的PLC连接重新加载入线程池
                        _plcConnectionPool.S7ReLoadPlcConnection(id, conn);
                    }
                    else
                    {
                        var conn = await _plcConnectionInfoRepository.GetByIdAsync(id);
                        if (conn != null)
                        {
                            _logUtil.Debug($"IP:{conn.IP}|EquipmentCode:{conn.ScEquipmentCode}|当前PLC状态未连接启用重连...");
                            //将断开的PLC连接重新加载入线程池
                            _plcConnectionPool.S7ReLoadPlcConnection(id, conn);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logUtil.Error($"IPID:{id}当前PLC状态未连接，启动重连报错Redis已经无连接数据...{ex.Message}");
                }
            }
        }

        /// <summary>
        /// 插入或更新锁定作业
        /// </summary>
        /// <param name="plcConnectionID"></param>
        /// <param name="jobName"></param>
        /// <returns></returns>

        public PlcConnectionS7 S7GetReadPlcConnection(long plcConnectionID, string jobName, string programeCode)
        {

            List<PlcConnectionLockInfo> plclocks = new();
            PlcConnectionLockInfo plclock = new();
            //string programeCode = _configuration["MyPrograme:ProgrameName"];
            var s7PlcConnection = _plcConnectionPool.S7GetReadPlcConnection(plcConnectionID, jobName);
            try
            {

                if (s7PlcConnection == null)
                {
                    //_logUtil.Info($"作业jobName{jobName}");
                    return null;
                }
                //plclock = _plcConnectionLockInfoRepository.GetFirst(l => l.PlcConnectionID == plcConnectionID && l.ProgrameCode == programeCode && l.JobName == jobName);
                plclocks = _IRedisHelper.GetStringKey<List<PlcConnectionLockInfo>>("PlcConnectionLockInfo");
                if (plclocks != null&& plclocks.Count !=0)
                    plclock = plclocks.Where(l => l.PlcConnectionID == plcConnectionID && l.ProgrameCode == programeCode && l.JobName == jobName).Select(l => l).FirstOrDefault();
                if (plclocks == null || plclocks.Count == 0 )
                {
                    plclocks = _plcConnectionLockInfoRepository.GetList();
                    plclock = plclocks.Where(l => l.PlcConnectionID == plcConnectionID && l.ProgrameCode == programeCode && l.JobName == jobName).Select(l => l).FirstOrDefault();
                    //_IRedisHelper.Delete("PlcConnectionLockInfo");
                    var saveOk = _IRedisHelper.SetStringKey("PlcConnectionLockInfo", plclocks,TimeSpan.FromMinutes(10));
                    //_logUtil.Info("Redis没有PlcConnectionLockInfo");
                }



                if (plclock != null)
                {
                    plclocks.Where(l => l.PlcConnectionID == plcConnectionID && l.ProgrameCode == programeCode && l.JobName == jobName).ForEach(l =>
                    {
                        l.IsLock = true;
                        l.UpdateTime = DateTime.Now;
                    });
                    var saveOk = _IRedisHelper.SetStringKey("PlcConnectionLockInfo", plclocks, TimeSpan.FromMinutes(10));
                    //_logUtil.Info("更新");

                }
                else
                {
                    plclock = new PlcConnectionLockInfo
                    {
                        ID = _snowflake.GetId(),
                        IsLock = true,
                        JobName = jobName,
                        PlcConnectionID = plcConnectionID,
                        ProgrameCode = programeCode,
                        UpdateTime = DateTime.Now
                    };
                    plclocks.Add(plclock);
                    var saveOk = _IRedisHelper.SetStringKey("PlcConnectionLockInfo", plclocks, TimeSpan.FromMinutes(10));
                    _plcConnectionLockInfoRepository.Insert(plclock);
                    
                }

            }
            catch (Exception ex)
            {
                _logUtil.Info($"作业jobName{jobName}-S7GetReadPlcConnection报错堆栈：{ex.StackTrace}"); 
                //_logUtil.Info($"s7PlcConnection{JsonConvert.SerializeObject(s7PlcConnection)}");
                //_logUtil.Info($"S7GetReadPlcConnection报错信息：{ex.Message}");
                //_logUtil.Info($"S7GetReadPlcConnection报错堆栈：{ex.StackTrace}");
            }



            //更新数据库锁定状态
            //var plcLock = _plcConnectionLockInfoRepository.GetFirst(pl => pl.PlcConnectionID == plcConnectionID && pl.ProgrameCode == programeCode && pl.JobName == jobName);

            //if (plcLock != null)
            //{
            //    plcLock.IsLock = true;
            //    plcLock.UpdateTime = DateTime.Now;
            //    _plcConnectionLockInfoRepository.Update(plcLock);//执行这个设备时锁定当前设备
            //}
            //else
            //{
            //    plcLock = new PlcConnectionLockInfo
            //    {
            //        ID = _snowflake.GetId(),
            //        IsLock = true,
            //        JobName = jobName,
            //        PlcConnectionID = plcConnectionID,
            //        ProgrameCode = programeCode,
            //        UpdateTime = DateTime.Now
            //    };
            //    _plcConnectionLockInfoRepository.Insert(plcLock);

            //}

            return s7PlcConnection;
        }


        //public PlcConnectionS7 S7GetReadPlcConnection(long plcConnectionID, string jobName, string programeCode)
        //{
        //    //string programeCode = _configuration["MyPrograme:ProgrameName"];
        //    var s7PlcConnection = _plcConnectionPool.S7GetReadPlcConnection(plcConnectionID, jobName);

        //    if (s7PlcConnection == null)
        //    {
        //        return null;
        //    }

        //    //更新数据库锁定状态
        //    var plcLock = _plcConnectionLockInfoRepository.GetFirst(pl => pl.PlcConnectionID == plcConnectionID && pl.ProgrameCode == programeCode && pl.JobName == jobName);

        //    if (plcLock != null)
        //    {
        //        plcLock.IsLock = true;
        //        plcLock.UpdateTime = DateTime.Now;
        //        _plcConnectionLockInfoRepository.Update(plcLock);//执行这个设备时锁定当前设备
        //    }
        //    else
        //    {
        //        plcLock = new PlcConnectionLockInfo
        //        {
        //            ID = _snowflake.GetId(),
        //            IsLock = true,
        //            JobName = jobName,
        //            PlcConnectionID = plcConnectionID,
        //            ProgrameCode = programeCode,
        //            UpdateTime = DateTime.Now
        //        };
        //        _plcConnectionLockInfoRepository.Insert(plcLock);

        //    }

        //    return s7PlcConnection;
        //}




        /// <summary>
        /// 返回PLC操作类
        /// </summary>
        /// <param name="plcConnectionID"></param>
        /// <param name="jobName"></param>
        /// <returns></returns>
        public PlcConnectionS7 S7GetReadPlcConnectionState(long plcConnectionID, string jobName, string programeCode)
        {
            //string programeCode = _configuration["MyPrograme:ProgrameName"];
            var s7PlcConnection = _plcConnectionPool.S7GetReadPlcConnection(plcConnectionID, jobName);

            if (s7PlcConnection == null)
            {
                return null;
            }
            return s7PlcConnection;
        }

        public bool S7FreeReadPlcConnection(long plcConnectionID, string jobName)
        {
            string programeCode = _configuration["MyPrograme:ProgrameName"];
            //更新锁定数据
            _plcConnectionLockInfoRepository.Update(pl => new PlcConnectionLockInfo { IsLock = false, UpdateTime = DateTime.Now }, pl => pl.PlcConnectionID == plcConnectionID && pl.ProgrameCode == programeCode && pl.JobName == jobName);

            return true;
        }
        public bool S7LockJobReadPlcConnection(string jobName)
        {
            return _plcConnectionPool.S7LockJobReadPlcConnection(jobName);
        }


        public bool S7FreeJobReadPlcConnection(string jobName, string programeCode)
        {
            List<PlcConnectionLockInfo> plclocks = new();
            PlcConnectionLockInfo plclock = new();
            //string programeCode = _configuration["MyPrograme:ProgrameName"];


            bool ret = false;
            try
            {
                ret = _plcConnectionPool.S7FreeJobReadPlcConnection(jobName);



                plclocks = _IRedisHelper.GetStringKey<List<PlcConnectionLockInfo>>("PlcConnectionLockInfo");
                if (plclocks != null&& plclocks.Count!=0)
                {
                    plclock = plclocks.Where(l => l.ProgrameCode == programeCode && l.JobName == jobName).Select(l => l).FirstOrDefault();
                }
                if (plclock == null || plclocks == null || plclocks.Count == 0)
                {
                    plclocks = _plcConnectionLockInfoRepository.GetList();
                    plclock = plclocks.Where(l => l.ProgrameCode == programeCode && l.JobName == jobName).Select(l => l).FirstOrDefault();
                    //_IRedisHelper.Delete("PlcConnectionLockInfo");
                    var saveOk = _IRedisHelper.SetStringKey("PlcConnectionLockInfo", plclocks, TimeSpan.FromMinutes(10));
                    //_logUtil.Info("Redis没有PlcConnectionLockInfo");
                }
                //更新锁定数据
                //_plcConnectionLockInfoRepository.Update(pl => new PlcConnectionLockInfo { IsLock = false, UpdateTime = DateTime.Now }, pl => pl.ProgrameCode == programeCode && pl.JobName == jobName);
                plclocks.Where(l => l.ProgrameCode == programeCode && l.JobName == jobName).ForEach(l =>
                {
                    l.IsLock = false;
                    l.UpdateTime = DateTime.Now;
                });
                _IRedisHelper.SetStringKey("PlcConnectionLockInfo", plclocks, TimeSpan.FromMinutes(10));
            }
            catch (Exception ex)
            {
                _logUtil.Error(ex.ToString() + ret.ToString() + jobName + programeCode);
            }
            return ret;
        }


        //public bool S7FreeJobReadPlcConnection(string jobName, string programeCode)
        //{
        //    bool ret = false;
        //    try
        //    {
        //        ret = _plcConnectionPool.S7FreeJobReadPlcConnection(jobName);
        //        //string programeCode = _configuration["MyPrograme:ProgrameName"];
        //        //更新锁定数据
        //        _plcConnectionLockInfoRepository.Update(pl => new PlcConnectionLockInfo { IsLock = false, UpdateTime = DateTime.Now }, pl => pl.ProgrameCode == programeCode && pl.JobName == jobName);
        //    }
        //    catch (Exception ex)
        //    {
        //        _logUtil.Error(ex.ToString());
        //    }
        //    return ret;
        //}

        /// <summary>
        /// 锁定连接检测
        /// </summary>
        /// <param name="jobName"></param>
        /// <returns></returns>
        public bool S7CheckJobLockedConnect(string jobName)
        {
            return _plcConnectionPool.S7CheckJobLockedConnect(jobName);
        }

        public PlcConnectionS7 S7GetWritePlcConnection(long plcConnectionID)
        {
            return _plcConnectionPool.S7GetWritePlcConnection(plcConnectionID);
        }

        public async Task S7Heartbeat()
        {

            string programeCode = _configuration["MyPrograme:ProgrameName"];
            var heartbeatDbs = await _plcReadDbInfoRepository.GetListAsync(pr => pr.IsHeartbeat && pr.IsEnable /*&&pr.PlcName==programeCode*/);//筛选对应作业心跳

            foreach (var heartbeatDb in heartbeatDbs)
            {
                if (!_plcConnectionPool.S7CheckIdExists(heartbeatDb.PlcConnectionID))
                {
                    continue;
                }
                bool isConnected = await _plcConnectionPool.S7Heartbeat(heartbeatDb);

                //连接状态更新到数据库
                var pConn = await _plcConnectionStateInfoRepository.GetFirstAsync(ps => ps.PlcConnectionID == heartbeatDb.PlcConnectionID && ps.ProgrameCode == programeCode);
                if (pConn != null)
                {
                    pConn.Connected = isConnected;
                    pConn.UpdateTime = DateTime.Now;
                    _plcConnectionStateInfoRepository.Update(pConn);
                }
                else
                {
                    pConn = new PlcConnectionStateInfo { ID = _snowflake.GetId(), PlcConnectionID = heartbeatDb.PlcConnectionID, ProgrameCode = programeCode, Connected = isConnected, UpdateTime = DateTime.Now };
                    _plcConnectionStateInfoRepository.Insert(pConn);
                }
            }



        }


        public async Task S7Heartbeat(List<string> programes)
        {

            //string programeCode = _configuration["MyPrograme:ProgrameName"];
            foreach (var item in programes)
            {
                try
                {

                    var heartbeat = _IRedisHelper.GetStringKey<List<PlcReadDbInfo>>("PlcReadDbInfo");

                    var heartbeatDbs = heartbeat.Where(l => l.IsHeartbeat && l.IsEnable);//await _plcReadDbInfoRepository.GetListAsync(pr => pr.IsHeartbeat && pr.IsEnable /*&&pr.PlcName==programeCode*/);//筛选对应作业心跳

                    foreach (var heartbeatDb in heartbeatDbs)
                    {
                        if (!_plcConnectionPool.S7CheckIdExists(heartbeatDb.PlcConnectionID))
                        {
                            continue;
                        }
                        bool isConnected = await _plcConnectionPool.S7Heartbeat(heartbeatDb);

                        //连接状态更新到数据库
                        var pConn = await _plcConnectionStateInfoRepository.GetFirstAsync(ps => ps.PlcConnectionID == heartbeatDb.PlcConnectionID /*&& ps.ProgrameCode == item*/);
                        if (pConn != null)
                        {
                            pConn.Connected = isConnected;
                            pConn.UpdateTime = DateTime.Now;
                            _plcConnectionStateInfoRepository.Update(pConn);
                            _plcConnectionStateInfoRepository.AsSugarClient().Dispose();
                            //await Task.CompletedTask;
                        }
                        else
                        {
                            pConn = new PlcConnectionStateInfo { ID = _snowflake.GetId(), PlcConnectionID = heartbeatDb.PlcConnectionID, ProgrameCode = item, Connected = isConnected, UpdateTime = DateTime.Now };
                            _plcConnectionStateInfoRepository.Insert(pConn);
                            _plcConnectionStateInfoRepository.AsSugarClient().Dispose();
                            //await Task.CompletedTask;
                        }
                    }
                }
                catch
                {
                    await Task.CompletedTask;
                }



            }

        }
    }
}
